package pt.compflow.activity.task;

import java.util.Iterator;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import pt.compflow.Context;
import pt.compflow.flowontology.model.Assignment;
import pt.compflow.flowontology.model.FLOWO;
import pt.compflow.flowontology.model.FlowOntologyModel;
import pt.compflow.flowontology.model.Job;
import pt.compflow.flowontology.model.Loop;
import pt.compflow.flowontology.model.Task;
import pt.compflow.flowontology.model.Workflow;
import pt.compflow.graph.Tree;
import pt.compflow.tools.OntModelHelper;

import com.hp.hpl.jena.ontology.OntClass;
import com.hp.hpl.jena.ontology.OntProperty;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.vocabulary.RDF;
import com.hp.hpl.jena.vocabulary.RDFS;

public class InputFinderImpl implements InputFinder {
	
	private static final Logger LOG = LoggerFactory.getLogger(InputFinderImpl.class);

	@Override
	public void setInputAndContext(Context c, OntModelHelper ontModelHelper, FlowOntologyModel engineModel, FlowOntologyModel jobModel, Job job, Workflow workflow, Task t) {
		
		// #### LOOP CODE
		int iteration = -1;
		if(workflow.isLoop()) {
			iteration = workflow.as(Loop.class).getIteration();
		}
		// #### LOOP CODE
		
		Resource taskClass = ontModelHelper.getDirectLeafType(t, FLOWO.Task.getURI());
		
		// set input and context
		OntProperty inputProperty = ontModelHelper.getFirstApplicableSubProperty(taskClass.getURI(), FLOWO.hasInput.getURI(), true);
			
		Tree inputTree = ontModelHelper.getApplicableEdgeDestinationsClosure(inputProperty.getURI(), taskClass.getURI(), FLOWO.Task.getURI());
		if(inputTree != null && !inputTree.isEmpty()) {
			// get instances of the classes 

			Iterator<Tree> it = inputTree.iterator();
			while(it.hasNext()) {
				Tree tree = it.next();
				
				// all instances of the input domain class become the input of the task
				Resource origInputClass = ontModelHelper.getOntModel().getResource(tree.getNode());
				Resource inputClass = iteration >= 0 ? ontModelHelper.getOntModel().createClass(tree.getNode() + "_" + iteration) : origInputClass;
				
				// #### LOOP CODE
				if(inputClass != origInputClass) {
					inputClass.addProperty(RDFS.subClassOf, origInputClass);
					
					LOG.debug("Adding type " + inputClass.getURI() + " to instances of " + origInputClass.getURI() + " as the input of task " + t.getURI() + " ( " + taskClass.getURI() + " )");
					StmtIterator stmtIt = jobModel.listStatements(null, RDF.type, origInputClass);
					while(stmtIt.hasNext()) {
						Resource domainInstance = stmtIt.next().getSubject();
						domainInstance.addProperty(RDF.type, inputClass);
					}
				}
				// #### LOOP CODE
				
				List<OntClass> domainClasses = ontModelHelper.listDirectLeafSuperClasses(tree.getNode(), null);
				for(Resource domainClass : domainClasses) {
					LOG.debug("Adding type " + inputClass.getURI() + " to instances of " + domainClass.getURI() + " as the input of task " + t.getURI() + " ( " + taskClass.getURI() + " )");
					StmtIterator stmtIt = jobModel.listStatements(null, RDF.type, domainClass);
					while(stmtIt.hasNext()) {
						Resource domainInstance = stmtIt.next().getSubject();
						domainInstance.addProperty(RDF.type, inputClass);
						if(inputClass != origInputClass) domainInstance.addProperty(RDF.type, origInputClass);
					}
				}
				
				LOG.debug("Setting instances of " + inputClass.getURI() + " as the input of task " + t.getURI() + " ( " + taskClass.getURI() + " )");
				StmtIterator stmtIt = jobModel.listStatements(null, RDF.type, inputClass);
				while(stmtIt.hasNext()) {
					Resource input = stmtIt.next().getSubject();
					t.addProperty(inputProperty, input);
					t.addProperty(FLOWO.hasInput, input);
				}
			}
		}
		
	}

	@Override
	public void setInputAndContext(Context c, OntModelHelper ontModelHelper, FlowOntologyModel engineModel,
			FlowOntologyModel jobModel, Job job, Workflow workflow, Task task, Assignment assignment, Resource unit) {
		
		// #### LOOP CODE
		int iteration = -1;
		if(workflow.isLoop()) {
			iteration = workflow.as(Loop.class).getIteration();
			
			Resource assignmentClass = ontModelHelper.getDirectLeafType(assignment, FLOWO.Assignment.getURI());
			Resource newAssignmentClass = ontModelHelper.getOntModel().createClass(assignmentClass.getURI() + "_" + iteration);
			newAssignmentClass.addProperty(RDFS.subClassOf, assignmentClass);
			
			assignment.addProperty(RDF.type, newAssignmentClass);
		}
		// #### LOOP CODE
		
		Resource taskClass = ontModelHelper.getDirectLeafType(task, FLOWO.Task.getURI());
		assignment.addProperty(ontModelHelper.getFirstApplicableSubProperty(taskClass.getURI(), FLOWO.hasInput.getURI(), true), unit);
		assignment.addProperty(FLOWO.hasInput, unit);
	}

}
